06 바인드 변수의 부작용과 해법

  • 바인드 변수 사용 시 Sql의 수행 절차
    1. 최초수행시점에 최적화를 거친 실행계획이 캐시에 적재된다.
    2. 실행시점에 공유커서를 가져와 조건값만 다르게 바인딩 하면서 반복 재사용하게 된다.
  • 바인드 변수 사용 시 문재점
    1. 평균 분포를 가정한 실행계획 수립
    2. 컬럼 분포가 균일할 경우 문제될것 없다.
    3. 컬럼 분포가 균일하지 않을 경우 실행계획은 바인딩되는 값에 따라 최적일수도 최악일수도 있다.
    4. 등치(=)조건이 아닌 부등호나 범위검색일 경우 고정된 규칙을 사용하여 실행계획을 수립한다.
번호바인딩 조건 형태선택도(Selectivity)
1번호 > :no5%
2번호 < :no5%
3번호 >= :no5%
4번호 <= :no5%
5번호 BETWEEN :no1 AND :no20.25%
6번호 > :no1 AND 번호 <= :no20.25%
7번호 >= :no1 AND 번호 < :no20.25%
8번호 > :no1 AND 번호 < :no20.25%
  • Cardinality : 비용계산시 특정 엑세스 단계를 거치고 출력될것으로 예상되는 건수
  • Cardinality = 선택도(Selectivity) * 전체레코드수
  • 바인딩 조건형태에 따른 카디널리티 예측 테스트



SQL> CREATE TABLE t
  2  AS
  3  SELECT ROWNUM no
  4    FROM dual
  5  CONNECT BY LEVEL <= 1000
  6  ;

테이블이 생성되었습니다.

SQL> ANALYZE TABLE t COMPUTE STATISTICS FOR TABLE FOR ALL COLUMNS;

테이블이 분석되었습니다.

SQL> EXPLAIN PLAN FOR SELECT * FROM t WHERE no <= :no;

해석되었습니다.

SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY(null, null, 'basic rows'));

Plan hash value: 1601196873

------------------------------------------
| Id  | Operation         | Name | Rows  |
------------------------------------------
|   0 | SELECT STATEMENT  |      |    50 |
|   1 |  TABLE ACCESS FULL| T    |    50 |
------------------------------------------

8 개의 행이 선택되었습니다.

SQL> EXPLAIN PLAN FOR SELECT * FROM t WHERE no BETWEEN :no1 AND :no2;

해석되었습니다.

SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY(null, null, 'basic rows'));

Plan hash value: 1322348184

-------------------------------------------
| Id  | Operation          | Name | Rows  |
-------------------------------------------
|   0 | SELECT STATEMENT   |      |     3 |
|   1 |  FILTER            |      |       |
|   2 |   TABLE ACCESS FULL| T    |     3 |
-------------------------------------------

9 개의 행이 선택되었습니다.



  • 검색 Row 수 예측 : 실제와 많이 다를 수 있다.
    1. 부등호 검색 카디널리티 = 1000 * 5 / 100 = 50
    2. 범 위 검색 카디널리티 = 1000 * 0.25 / 100 = 2.5 = 3
  • 리터럴 상수 조건에 따른 카디널리티 예측 테스트 : 거의 정확한 로우수 예측



SQL> EXPLAIN PLAN FOR SELECT * FROM t WHERE no <= 100;

해석되었습니다.

SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY(null, null, 'basic rows'));

Plan hash value: 1601196873

------------------------------------------
| Id  | Operation         | Name | Rows  |
------------------------------------------
|   0 | SELECT STATEMENT  |      |    99 |
|   1 |  TABLE ACCESS FULL| T    |    99 |
------------------------------------------

8 개의 행이 선택되었습니다.

SQL> EXPLAIN PLAN FOR SELECT * FROM t WHERE no BETWEEN 500 AND 600;

해석되었습니다.

SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY(null, null, 'basic rows'));

Plan hash value: 1601196873

------------------------------------------
| Id  | Operation         | Name | Rows  |
------------------------------------------
|   0 | SELECT STATEMENT  |      |    99 |
|   1 |  TABLE ACCESS FULL| T    |    99 |
------------------------------------------

8 개의 행이 선택되었습니다.



(1) 바인드 변수 Peeking

(2) 적응적 커서 공유(Adaptive Cursor Sharing)

(3) 입력값에 따라 SQL 분리


SELECT /*+ FULL(a) */ *
  FROM 아파트매물 a
 WHERE :City IN ('서울시', '경기도')
   AND 도시 = :City
 UNION ALL
SELECT /*+ INDEX(a idx01) */ *
  FROM 아파트매물 a
 WHERE :City NOT IN ('서울시', '경기도')
   AND 도시 = :City
;




IF :City IN ('서울시', '경기도') THEN
  SELECT /*+ FULL(a) */ *
    FROM 아파트매물 a
   WHERE 도시 = :City
  ;
ELSE
  SELECT /*+ INDEX(a idx01) */ *
    FROM 아파트매물 a
   WHERE 도시 = :City
;
END IF;



(4) 예외적으로, Literal 상수값 사용